package com.asiainfo.complier.editor;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;

import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JViewport;
import javax.swing.KeyStroke;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

import com.asiainfo.complier.astyle.AStyle;
import com.asiainfo.complier.common.ClassInfo;
import com.asiainfo.complier.common.CodeInfo;
import com.asiainfo.complier.common.ComplierInfo;
import com.asiainfo.complier.common.OutLineInfo;
import com.asiainfo.complier.helper.JavaOutLineHelper;
import com.asiainfo.complier.helper.UIHelper;
import com.asiainfo.complier.model.NameAndPictureListCellRenderer;
import com.asiainfo.complier.ui.ErrorPane;

public class CoderEditor extends JPanel {
	/**
	 * CoderEditor
	 */
	private static final long serialVersionUID = -8362955176164536265L;

	private int editCurLine = -1;
	private LineEditor editor;

	public LineEditor getEditor() {
		return editor;
	}

	private Object highterObject;
	private SmartIndent indent;
	private ToolTipList outLineList;
	private JSplitPane mainJPanel;
	private ErrorPane infoPanel;
	private ErrorPane imgInfoPanel;
	private int startLine = 0;
	private int endLine = 0;
	private JScrollPane editorPane;
	private LinePanel linePanel;
	private CaretPanel caretPanel;

	private boolean showLineHighter = true;

	private CodeInfo info;

	/**
	 * @return the info
	 */
	public CodeInfo getCodeInfo() {
		return info;
	}

	private ICallBack caretCallBack;

	/**
	 * @return the caretCallBack
	 */
	public ICallBack getCaretCallBack() {
		return caretCallBack;
	}

	/**
	 * @param caretCallBack
	 *            the caretCallBack to set
	 */
	public void setCaretCallBack(ICallBack caretCallBack) {
		this.caretCallBack = caretCallBack;
	}

	/**
	 * @param info
	 *            the info to set
	 */
	public void setCodeInfo(CodeInfo info) {
		this.info = info;
	}

	private JPopupMenu initPopupMenu() {
		JPopupMenu popupMenu = new JPopupMenu();
		JMenuItem cutFileItem = new JMenuItem(UIHelper.getResource("lan_cut"));
		cutFileItem.setIcon(UIHelper
				.getImageIco("com/asiainfo/images/menu-cut.png"));
		cutFileItem.setAccelerator(KeyStroke.getKeyStroke('X',
				java.awt.Event.CTRL_MASK, false));

		cutFileItem.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				editor.cut();

			}
		});
		JMenuItem copyItem = new JMenuItem(UIHelper.getResource("lan_copy"));
		copyItem.setAccelerator(KeyStroke.getKeyStroke('C',
				java.awt.Event.CTRL_MASK, false));
		copyItem.setIcon(UIHelper
				.getImageIco("com/asiainfo/images/menu-copy.png"));
		copyItem.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				editor.copy();
			}
		});
		JMenuItem pasteItem = new JMenuItem(UIHelper.getResource("lan_paste"));
		pasteItem.setAccelerator(KeyStroke.getKeyStroke('V',
				java.awt.Event.CTRL_MASK, false));
		pasteItem.setIcon(UIHelper
				.getImageIco("com/asiainfo/images/menu-paste.png"));
		pasteItem.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				editor.paste();
			}
		});

		JMenuItem formatItem = new JMenuItem(UIHelper.getResource("lan_format"));
		formatItem.setAccelerator(KeyStroke.getKeyStroke('F',
				java.awt.Event.CTRL_MASK | Event.SHIFT_MASK, true));
		formatItem.setIcon(UIHelper
				.getImageIco("com/asiainfo/images/menu-paste.png"));
		formatItem.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				int pos = editor.getCaretPosition();
				String code = editor.getText();

				editor.setText(AStyle.doFormat(code));
				if (pos < editor.getText().length()) {
					editor.moveCaretPosition(pos);
					editor.select(pos, 0);
				}

			}
		});
		popupMenu.add(cutFileItem);
		popupMenu.add(copyItem);
		popupMenu.add(pasteItem);
		popupMenu.add(formatItem);

		return popupMenu;
	}

	/**
	 * update error message
	 * 
	 * @param complierInfo
	 */
	public void updateErrorInfo(ArrayList<ComplierInfo> complierInfo) {
		infoPanel.setComplierInfo(complierInfo);
		infoPanel.UpdateError(startLine, endLine);

		imgInfoPanel.setComplierInfo(complierInfo);
		imgInfoPanel.UpdateImageError(startLine, endLine);
		linePanel.UpdateLineInfo(startLine, endLine);
	}

	/**
	 * get caret word
	 * 
	 * @return
	 */

	public String getCaretWord() {
		int pos = editor.getCaret().getDot();

		if (pos < 0)
			return null;
		int index = pos;

		while (index >= 0) {
			index--;

			try {
				String Str = editor.getDocument().getText(index, 1);
				char c = Str.charAt(0);

				if (c == ' ' || c == '\r' || c == '\n' || c == '\t')
					break;
			} catch (BadLocationException e) {

				e.printStackTrace();
			}

		}
		// pos = editor.getCaret().getDot();

		try {
			String result = editor.getDocument()
					.getText(index + 1, pos - index + 3).trim();
			// System.out.println("result=" + result);
			return result;
		} catch (Exception e) {

			e.printStackTrace();
			return "";
		}
	}

	private void UpdateOutLine(LineEditor mEditor) {
		String sourceCode = mEditor.getText();
		JavaOutLineHelper outLine = new JavaOutLineHelper();
		ClassInfo classInfo = outLine.parseOutLine(sourceCode);
		ArrayList<OutLineInfo> outList = classInfo.getOutLineInfo();

		OutLineInfo rootInfo = new OutLineInfo(0);
		rootInfo.setMethodName("Outline");

		DefaultMutableTreeNode rootTreeNode = new DefaultMutableTreeNode();
		rootTreeNode.setUserObject(rootInfo);

		DefaultTreeModel listModel = new DefaultTreeModel(rootTreeNode);
		int index = 0;

		DefaultMutableTreeNode classNode = new DefaultMutableTreeNode(
				classInfo.getClassName());
		OutLineInfo classNodeInfo = new OutLineInfo(2);
		classNodeInfo.setMethodName(classInfo.getClassName() + "["
				+ outList.size() + "]");
		classNode.setUserObject(classNodeInfo);

		for (OutLineInfo info : outList) {
			DefaultMutableTreeNode node = new DefaultMutableTreeNode(info);
			node.setUserObject(info);
			listModel.insertNodeInto(node, classNode, index);
			index++;
		}
		DefaultMutableTreeNode packeTreeNode = new DefaultMutableTreeNode(
				classInfo.getPackName());
		index = 0;
		for (OutLineInfo info : classInfo.getImportList()) {
			DefaultMutableTreeNode node = new DefaultMutableTreeNode(info);
			node.setUserObject(info);
			listModel.insertNodeInto(node, packeTreeNode, index);
			index++;
		}

		OutLineInfo packInfo = new OutLineInfo(1);
		packInfo.setMethodName(classInfo.getPackName());
		packeTreeNode.setUserObject(packInfo);

		listModel.insertNodeInto(packeTreeNode, rootTreeNode, 0);
		listModel.insertNodeInto(classNode, rootTreeNode, 1);
		outLineList.setToolTipText("Outline");
		outLineList.setShowsRootHandles(false);
		outLineList.setModel(listModel);
		TreePath parentPath = new TreePath(classNode.getPath());
		outLineList.expandPath(parentPath);
		outLineList.setCellRenderer(new NameAndPictureListCellRenderer());

		mainJPanel.setDividerLocation(200);

	}

	public CoderEditor(CodeInfo info) {

		editor = new LineEditor();
		editor.setText(" ");
		final JPopupMenu popMenu = initPopupMenu();
		final WordHighter wordHighter = new WordHighter(editor);
		final CaretHighter caretHighter = new CaretHighter(editor);
		new AutoCompleteUI(this);

		outLineList = new ToolTipList();

		mainJPanel = new JSplitPane();
		JScrollPane leftPane = new JScrollPane();
		leftPane.getViewport().add(outLineList);
		UpdateOutLine(editor);
		editorPane = new JScrollPane();
		outLineList.addTreeSelectionListener(new TreeSelectionListener() {

			public void valueChanged(TreeSelectionEvent e) {
				TreePath path = e.getNewLeadSelectionPath();
				if (path == null)
					return;
				DefaultMutableTreeNode node = (DefaultMutableTreeNode) path
						.getLastPathComponent();//
				if (node == null)
					return;
				OutLineInfo info = (OutLineInfo) node.getUserObject();
				if (info != null) {
					if (info.getStartPos() != -1) {
						int curLine = editor.getLineOfOffset(info.getStartPos());
						go2Line(curLine);
						highterRangle(info.getStartPos(), info.getEndPos());
					}
				}
			}
		});

		mainJPanel.setLeftComponent(leftPane);

		infoPanel = new ErrorPane(this,true);
		imgInfoPanel = new ErrorPane(this,false);
		infoPanel.setPreferredSize(new Dimension(16, -1));
		imgInfoPanel.setPreferredSize(new Dimension(16, -1));

		editorPane.getViewport().add(editor);
		editorPane.setBorder(null);
		linePanel = new LinePanel(this);
		editorPane.getViewport().addChangeListener(new ChangeListener() {

			public void stateChanged(ChangeEvent e) {
				JViewport viewPort = (JViewport) e.getSource();

				// System.out.println(viewPort.getExtentSize().width +"  ==  "+
				// viewPort.getExtentSize().height);
				// System.out.println(infoPanel.getWidth() +"  ==  "+
				// infoPanel.getHeight());
				Point vp = viewPort.getViewPosition();
				// System.out.println(vp.x + " == " + vp.y);
				// System.out.println(vp.x + " == " + vp.y
				// /editor.getRowHeight());
				startLine = vp.y / editor.getRowHeight() + 1;
				endLine = (vp.y + viewPort.getExtentSize().height)
						/ editor.getRowHeight();

				infoPanel.UpdateError(startLine, endLine);
				imgInfoPanel.UpdateImageError(startLine, endLine);
				linePanel.UpdateLineInfo(startLine, endLine);

			}
		});

		caretPanel = new CaretPanel();
		JPanel leftCodePanel = new JPanel();
		leftCodePanel.setLayout(new BorderLayout());
		leftCodePanel.add(imgInfoPanel, BorderLayout.WEST);
		leftCodePanel.add(linePanel, BorderLayout.CENTER);
		leftCodePanel.add(caretPanel, BorderLayout.EAST);

		JPanel rightPane = new JPanel();
		rightPane.setLayout(new BorderLayout());
		rightPane.add(editorPane, BorderLayout.CENTER);
		rightPane.add(infoPanel, BorderLayout.EAST);
		rightPane.add(leftCodePanel, BorderLayout.WEST);
		mainJPanel.setRightComponent(rightPane);
		this.setLayout(new BorderLayout());

		this.add(mainJPanel, BorderLayout.CENTER);

		this.info = info;
		final DefaultHighlighter highter = new DefaultHighlighter();
		editor.setHighlighter(highter);

		editor.getDocument().addDocumentListener(new DocumentListener() {

			public void removeUpdate(DocumentEvent e) {

				UpdateOutLine(editor);
			}

			public void insertUpdate(DocumentEvent e) {

				UpdateOutLine(editor);
			}

			public void changedUpdate(DocumentEvent e) {

				UpdateOutLine(editor);
			}
		});
		editor.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				int offsetX = editor.getOffsetX();
				if (MouseEvent.BUTTON3 == e.getButton()) {
					if (e.getX() >= offsetX)
						popMenu.show(editor, e.getX(), e.getY());
				}
				wordHighter.removeAllHighter();
				wordHighter.addWordHighter();
				if (objHiObject != null) {
					editor.getHighlighter().removeHighlight(objHiObject);
					objHiObject = null;
				}

			}
		});

		editor.addCaretListener(new CaretListener() {

			public void caretUpdate(CaretEvent e) {

				caretHighter.removeAllHighter();
				caretHighter.addWordHighter();
				final int dot = e.getDot();

				if (showLineHighter) {
					int offsetX = 10 /* editor.getOffsetX() */;
					try {
						int curLine = editor.getLineOfOffset(dot);
						int endIndex = editor.getLineOfOffset(curLine);
						int startIndex = editor.getLineEndOfOffset(dot);

						if (caretCallBack != null)
							caretCallBack.doWOrk("Row:" + (curLine) + " Col:"
									+ (dot - startIndex + 1));
						if (editCurLine == curLine)
							return;
						editCurLine = curLine;
						if (null != highterObject)
							highter.removeHighlight(highterObject);
						highterObject = highter.addHighlight(startIndex,
								endIndex, new LineHighter(dot, offsetX));

					} catch (BadLocationException e1) {

						e1.printStackTrace();
					}

				}
			}
		});

	}

	public LineEditor getActiveEditor() {
		return editor;
	}

	public void setShowLineNumber(boolean lineNumber) {

		editor.setShowLineNumber(lineNumber);
	}

	/**
	 * show line
	 * 
	 * @return
	 */
	public boolean getShowLineNumber() {
		return editor.getShowLineNumber();
	}

	/**
	 * @param text
	 */
	public void setText(String text) {
		editor.setText(text);
	}

	public void setShowLineHighter(boolean highterLine) {
		this.showLineHighter = highterLine;
		editor.repaint();
	}

	/**
	 * @return
	 */
	public String getText() {
		return editor.getText();
	}

	/**
	 * go to line
	 * 
	 * @param curLine
	 */
	public void go2Line(int curLine) {

		int rowHeight = editor.getRowHeight();
		JViewport vp = editorPane.getViewport();
		int offset = editor.getOffsetOfLine(curLine);
		int offsetLine = vp.getVisibleRect().height / rowHeight / 2;
		int y = (curLine - offsetLine) * rowHeight;
		int x = 0;
		editor.setSelectionStart(offset);
		editor.setSelectionEnd(offset + 1);
		//System.out.println("y=" + y);
		if (y > 0) {
			vp.setViewPosition(new Point(x, y));
		}

	}

	private Object objHiObject;

	/**
	 * Higher Rangle
	 * 
	 * @param startIndex
	 * @param endIndex
	 * @return
	 */
	public boolean highterRangle(int startIndex, int endIndex) {

		LineEditor editor2 = this.getActiveEditor();
		try {
			if (objHiObject != null) {
				editor2.getHighlighter().removeHighlight(objHiObject);
				objHiObject = null;
			}
			objHiObject = editor2.getHighlighter().addHighlight(startIndex,
					endIndex, new WordHighter(editor2));
		} catch (BadLocationException e) {

			e.printStackTrace();
		}
		return true;
	}

}

class MyHighlightPainter extends DefaultHighlighter.DefaultHighlightPainter {
	public MyHighlightPainter(Color color) {
		super(color);
	}
}
